package io.github.wslxm.springbootplus2.manage.sys.service.impl;

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.github.wslxm.springbootplus2.core.base.model.BasePage;
import io.github.wslxm.springbootplus2.core.base.service.impl.BaseServiceImpl;
import io.github.wslxm.springbootplus2.core.config.error.ErrorException;
import io.github.wslxm.springbootplus2.core.enums.BaseDic;
import io.github.wslxm.springbootplus2.core.result.ResultType;
import io.github.wslxm.springbootplus2.core.utils.XjBeanUtil;
import io.github.wslxm.springbootplus2.core.utils.tree.XjTreeUtil;
import io.github.wslxm.springbootplus2.core.utils.validated.XjValidUtil;
import io.github.wslxm.springbootplus2.manage.sys.mapper.SysUserMapper;
import io.github.wslxm.springbootplus2.manage.sys.model.dto.SysUserDTO;
import io.github.wslxm.springbootplus2.manage.sys.model.entity.SysDep;
import io.github.wslxm.springbootplus2.manage.sys.model.entity.SysUser;
import io.github.wslxm.springbootplus2.manage.sys.model.query.SysUserQuery;
import io.github.wslxm.springbootplus2.manage.sys.model.vo.SysDepVO;
import io.github.wslxm.springbootplus2.manage.sys.model.vo.SysUserRolesVO;
import io.github.wslxm.springbootplus2.manage.sys.model.vo.SysUserVO;
import io.github.wslxm.springbootplus2.manage.sys.service.SysDepService;
import io.github.wslxm.springbootplus2.manage.sys.service.SysRoleUserService;
import io.github.wslxm.springbootplus2.manage.sys.service.SysUserService;
import io.github.wslxm.springbootplus2.starter.redis.lock.XjDistributedLock;
import io.github.wslxm.springbootplus2.starter.websocket.model.vo.OnlineUserVO;
import io.github.wslxm.springbootplus2.starter.websocket.service.WebsocketService;
import io.github.wslxm.springbootplus2.utils.JwtUtil;
import io.github.wslxm.springbootplus2.utils.XjMd5Util;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wangsong
 */
@Service
public class SysUserServiceImpl extends BaseServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    @Autowired
    private SysRoleUserService roleUserService;

    @Autowired
    private SysDepService depService;

    @Autowired
    private WebsocketService websocketService;

    @Override
    public BasePage<SysUserVO> findPage(SysUserQuery query) {
        LinkedList<String> fieldNames = super.getFieldNames(true);
        XjValidUtil.isTrue(!fieldNames.contains(query.getSort()), "排序字段错误");

        if (query.getIsLoginUser() == null) {
            query.setIsLoginUser(false);
        }
        // 通过 在线/不在线 查询
        if (query.getIsOnline() != null) {
            query.setOnlineUserIds(this.getOnlineUserIds(query.getIsOnline()));
        }
        // 是否只查询当前登录人创建的用户
        String createUserId = query.getIsLoginUser() ? JwtUtil.getJwtUser(request).getUserId() : null;
        Page<SysUserVO> page = new Page<>(query.getCurrent(), query.getSize());
        Page<SysUserVO> pageVo = page.setRecords(baseMapper.list(page, query, createUserId));

        // 查询并设置角色信息是否在线等信息
        if (pageVo.getRecords() != null && pageVo.getRecords().size() > 0) {
            List<String> userIds = pageVo.getRecords().stream().map(SysUserVO::getId).collect(Collectors.toList());
            Map<String, List<SysUserRolesVO>> userRolesMap = roleUserService.findUserRoles(userIds);
            for (SysUserVO sysUserVO : pageVo.getRecords()) {
                // 当前是否在线
                sysUserVO.setIsOnline(websocketService.isOnline(sysUserVO.getId()));
                // 设置角色信息
                sysUserVO.setRoles(userRolesMap.get(sysUserVO.getId()));
                // 角色id组装便于角色回显
                sysUserVO.setRoleIds(sysUserVO.getRoles() == null ? null : sysUserVO.getRoles().stream().map(SysUserRolesVO::getRoleId).collect(Collectors.toList()));
                sysUserVO.setRoleCodes(sysUserVO.getRoles() == null ? null : sysUserVO.getRoles().stream().map(SysUserRolesVO::getCode).collect(Collectors.toList()));
            }
        }
        return XjBeanUtil.pageVo(pageVo);
    }

    /**
     * 获取在线用户Ids
     * @param isOnline true-查询在线用户 false-查询不在线用户
     * @return
     */
    private Set<String> getOnlineUserIds(Boolean isOnline) {
        Map<String, OnlineUserVO> onlineUsers = websocketService.getOnlineUsers();
        if (onlineUsers.size() > 0) {
            // 当前有在线用户 -->  在线 in 查询, 不在线 not in 查询
            return onlineUsers.keySet();
        } else {
            // 当前没有在线用户
            // 查询在线数据, 设置值为无法查询到数据的值, 让其返回空数据 id in("xxxxxxxx")
            // 查询不在线数据, 设置值为 null, 跳过查询
            Set<String> userIds = new HashSet<>();
            userIds.add("xxxxxxxx");
            return isOnline ? userIds : null;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @XjDistributedLock(lockName = "'xj-sys-user_'+ #dto.username +'_' + #dto.phone", waitTime = 5L)
    public String insert(SysUserDTO dto) {
        XjValidUtil.isStrLen(dto.getPassword(), 1, 20, "login.password.Length");
        // 判重账号/判重电话
        this.verifyRepeatUsername(dto.getUsername(), null);
        this.verifyRepeatPhone(dto.getPhone(), null);
        //
        SysUser adminUser = dto.convert(SysUser.class);
        adminUser.setId(IdUtil.getSnowflakeNextIdStr());
        adminUser.setPassword(XjMd5Util.encode(adminUser.getPassword(), adminUser.getId()));
        adminUser.setRegTime(LocalDateTime.now());
        if (dto.getDisable() == null) {
            // 如果未设置状态,默认启用状态
            adminUser.setDisable(BaseDic.Whether.V0.getValue());
        }
        this.save(adminUser);
        if (dto.getRoleIds() != null) {
            // 用户角色分配
            roleUserService.updUserRole(adminUser.getId(), dto.getRoleIds());
        }
        return adminUser.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @XjDistributedLock(lockName = "'xj-sys-user_'+ #dto.username +'_' + #dto.phone", waitTime = 5L)
    public Boolean upd(String id, SysUserDTO dto) {
        SysUser adminUser = this.getOne(new LambdaQueryWrapper<SysUser>()
                .select(SysUser::getUsername, SysUser::getPhone)
                .eq(SysUser::getId, id));
        XjValidUtil.isTrue(adminUser == null, ResultType.BASE_NO_RESP_DATA);

        // 判重账号/判重电话
        this.verifyRepeatUsername(dto.getUsername(), adminUser.getUsername());
        this.verifyRepeatPhone(dto.getPhone(), adminUser.getPhone());
        //
        SysUser entity = dto.convert(SysUser.class);
        entity.setId(id);
        this.updateById(entity);
        if (dto.getRoleIds() != null && dto.getRoleIds().size() > 0) {
            // 用户角色分配
            roleUserService.updUserRole(id, dto.getRoleIds());
        }
        return true;
    }

    @Override
    public Boolean del(String userId) {
        // 删除用户角色
        roleUserService.delByUserId(userId);
        return this.removeById(userId);
    }

    @Override
    public SysUserVO findId(String id) {
        // id查询数据
        SysUserQuery query = new SysUserQuery();
        query.setId(id);
        BasePage<SysUserVO> list = this.findPage(query);
        if (list.getRecords().size() == 0) {
            throw new ErrorException(ResultType.BASE_NO_RESP_DATA);
        }
        SysUserVO userVO = list.getRecords().get(0);

        // 公司/部门信息
        if (StringUtils.isNotBlank(userVO.getDepIds())) {
            String[] depIds = userVO.getDepIds().split(",");
            List<SysDep> deps = depService.list(new LambdaQueryWrapper<SysDep>().select(SysDep::getId, SysDep::getPid, SysDep::getName));
            List<SysDepVO> depVos = XjBeanUtil.listVo(deps, SysDepVO.class);
            userVO.setDep(XjTreeUtil.fatherTree(depVos, depIds[depIds.length - 1]));
            List<String> fatherNames = XjTreeUtil.getFatherNames(depVos, depIds[depIds.length - 1]);
            if (fatherNames != null && fatherNames.size() > 0) {
                userVO.setDepNames(StringUtils.join(fatherNames, "/"));
            }
        }
        return userVO;
    }

    @Override
    public List<SysUser> findByRoleId(String roleId) {
        return baseMapper.findByRoleId(roleId);
    }


    @Override
    public List<SysUserVO> listKeyData(String searchName) {
        List<SysUser> list = this.list(new LambdaQueryWrapper<SysUser>()
                .select(SysUser::getUsername, SysUser::getFullName, SysUser::getPhone, SysUser::getId)
                .orderByDesc(SysUser::getCreateTime)
                .and(com.baomidou.mybatisplus.core.toolkit.StringUtils.isNotBlank(searchName),
                        i -> i.like(SysUser::getFullName, searchName)
                                .or().like(SysUser::getUsername, searchName)
                )
        );
        return XjBeanUtil.listVo(list, SysUserVO.class);
    }


    @Override
    public Boolean updResetPassword(String id, String password) {
        XjValidUtil.isStrLen(password, 1, 20, "login.password.Length");
        return this.update(new SysUser(), new LambdaUpdateWrapper<SysUser>()
                .set(SysUser::getPassword, XjMd5Util.encode(password, id))
                .eq(SysUser::getId, id));
    }


    @Override
    public Boolean updByPassword(String oldPassword, String password) {
        XjValidUtil.isStrLen(password, 1, 20, "login.password.Length");
        SysUser adminUser = this.getById(JwtUtil.getJwtUser(request).getUserId());
        if (!adminUser.getPassword().equals(XjMd5Util.encode(oldPassword, adminUser.getId()))) {
            throw new ErrorException(ResultType.USER_PASSWORD_ERROR);
        }
        adminUser.setPassword(XjMd5Util.encode(password, adminUser.getId()));
        return this.updateById(adminUser);
    }


    /**
     * 验证账号是否重复
     *
     * @param username 新手机号
     * @return void
     * @author wangsong
     * @date 2021/9/30 0030 14:12
     * @version 1.0.1
     */
    private void verifyRepeatUsername(String username, String oldUserName) {
        if (StringUtils.isNotBlank(username)) {
            // 判重账号
            if (!username.equals(oldUserName)) {
                if (this.count(new LambdaQueryWrapper<SysUser>()
                        .eq(SysUser::getUsername, username)
                        .eq(SysUser::getDeleted, BaseDic.Whether.V0.getValue())
                ) > 0) {
                    throw new ErrorException(ResultType.USER_ACCOUNT_REPEAT);
                }
            }
        }
    }


    /**
     * 验证手机号是否重复
     *
     * @param phone    新手机号
     * @param oldPhone 原手机号(注册可不填)
     * @return void
     * @author wangsong
     * @date 2021/9/30 0030 14:11
     * @version 1.0.1
     */
    private void verifyRepeatPhone(String phone, String oldPhone) {
        if (StringUtils.isNotBlank(phone)) {
            // 判重电话
            if (!phone.equals(oldPhone)) {
                if (this.count(new LambdaQueryWrapper<SysUser>()
                        .eq(SysUser::getPhone, phone)
                        .eq(SysUser::getDeleted, BaseDic.Whether.V0.getValue())
                ) > 0) {
                    throw new ErrorException(ResultType.USER_PHONE_IS_DUPLICATE);
                }
            }
        }
    }
}
